import 'package:flutter/material.dart';
import 'package:lottie/lottie.dart';
import 'package:universally/universally.dart';

const _loading = 'assets/loading.json';

class LottiePage extends StatelessWidget {
  const LottiePage({super.key});

  @override
  Widget build(BuildContext context) {
    return BaseScaffold(
      appBarTitleText: 'Lottie',
      crossAxisAlignment: CrossAxisAlignment.center,
      child: FlRefreshScrollView(
        refreshConfig: FlEasyRefreshConfig(
          header: LottieHeader(),
          footer: LottieFooter(),
          onRefresh: (controller) async {
            await 3.seconds.delayed();
            controller(FlEasyRefreshResult.refreshSuccess);
          },
          onLoad: (controller) async {
            await 3.seconds.delayed();
            controller(FlEasyRefreshResult.loadingSuccess);
          },
        ),
        slivers: [],
      ),
    );
  }
}

class LottieHeader extends Header {
  LottieHeader({
    super.triggerOffset = 55,
    super.clamping = false,
    super.position,
    super.processedDuration = Duration.zero,
    super.readySpringBuilder,
    super.springRebound,
    super.frictionFactor,
    super.safeArea,
    super.infiniteOffset,
    super.hitOver,
    super.infiniteHitOver,
    super.hapticFeedback,
    super.triggerWhenReach,
    super.triggerWhenRelease,
    super.maxOverOffset,
  }) : super(
         spring: SpringDescription.withDampingRatio(
           mass: 0.5,
           stiffness: 600.0,
           ratio: 1.1,
         ),
       );

  @override
  Widget build(BuildContext context, IndicatorState state) {
    return LottieIndicator(state: state);
  }
}

class LottieFooter extends Footer {
  LottieFooter({
    super.triggerOffset = 55,
    super.clamping = false,
    super.position,
    super.processedDuration = const Duration(seconds: 0),
    super.readySpringBuilder,
    super.springRebound,
    super.safeArea,
    super.infiniteOffset = null,
    super.hitOver,
    super.infiniteHitOver,
    super.hapticFeedback,
    super.triggerWhenReach,
    super.triggerWhenRelease,
    super.maxOverOffset,
    super.frictionFactor,
  }) : super(
         spring: SpringDescription.withDampingRatio(
           mass: 0.5,
           stiffness: 600.0,
           ratio: 1.1,
         ),
       );

  @override
  Widget build(BuildContext context, IndicatorState state) {
    return LottieIndicator(state: state);
  }
}

class LottieIndicator extends StatefulWidget {
  const LottieIndicator({super.key, required this.state});

  final IndicatorState state;

  @override
  State<LottieIndicator> createState() => _LottieIndicatorState();
}

class _LottieIndicatorState extends State<LottieIndicator>
    with TickerProviderStateMixin {
  late AnimationController controller;

  IndicatorState get state => widget.state;

  @override
  void initState() {
    super.initState();
    controller = AnimationController(
      vsync: this,
      duration: Duration(milliseconds: 1500),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      height: state.offset >= 0 ? state.offset : 0,
      alignment: Alignment.center,
      color: Colors.grey,
      width: double.infinity,
      child: Lottie.asset(
        _loading,
        width: 40,
        height: 40,
        controller: controller,
        animate: false,
      ),
    );
  }

  @override
  void didUpdateWidget(covariant LottieIndicator oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (!context.mounted) return;
    animate();
  }

  IndicatorMode? lastMode;

  void animate() {
    if (lastMode == state.mode && state.mode != IndicatorMode.drag) return;
    lastMode = state.mode;
    switch (state.mode) {
      case IndicatorMode.inactive:
        controller.value = 0;
        break;
      case IndicatorMode.drag:
        final offset = state.offset;
        final triggerOffset = state.triggerOffset;
        if (offset <= triggerOffset) {
          controller.value = (offset / triggerOffset) / 2;
        }
        break;
      case IndicatorMode.armed:
        break;
      case IndicatorMode.ready:
      case IndicatorMode.processing:
        animation();
        break;
      case IndicatorMode.processed:
        controller.stop();
        break;
      case IndicatorMode.secondaryArmed:
        break;
      case IndicatorMode.secondaryReady:
        break;
      case IndicatorMode.secondaryOpen:
        break;
      case IndicatorMode.secondaryClosing:
        break;
      case IndicatorMode.done:
        controller.reset();
        break;
    }
  }

  Future<void> animation() async {
    if (controller.isAnimating) return;
    await controller.repeat();
  }

  @override
  void dispose() {
    super.dispose();
    controller.dispose();
  }
}
